/*
Copyright 2007 Julian Asamer

This file is part of Fractalicious.

    Fractalicious is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Fractalicious is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Fractalicious.  If not, see <http://www.gnu.org/licenses/>.
*/

#import <Foundation/Foundation.h>
#import "vector.h"
#import "math.h"

@implementation Vector 	
//Vector2D related Code
	+(Vector2D) makeVector {
		Vector2D p;
		return p;
	}
	+(Vector2D) makeVectorWithvx: (float) vx vy: (float) vy {
		Vector2D v;
		v.vx = vx;
		v.vy = vy;
		return v;
	}
	
	+(Vector2D) addVector: (Vector2D) v toVector: (Vector2D) w {	
		w.vx += v.vx;
		w.vy += v.vy;
		return w;
	}
	+(Vector2D) subtractVector: (Vector2D) v fromVector: (Vector2D) w {
		w.vx -= v.vx;
		w.vy -= v.vy;
		return w;
	}
	+ (Vector2D) reverseVector: (Vector2D) v {
		v.vx *= -1;
		v.vx *= -1;
		return v;
	}
	+ (Vector2D) multiplyVector: (Vector2D) v with: (float) factor {
		v.vx *= factor;
		v.vy *= factor;
		return v;
	}
	+ (Vector2D) divideVector: (Vector2D) v through: (float) divisor {
		v.vx /= divisor;
		v.vy /= divisor;
		return v;
	}
	+ (float) lengthOfVector: (Vector2D) v {
		return sqrt(v.vx*v.vx+v.vy*v.vy);
	}
	+ (Vector2D) unityVectorOfVector: (Vector2D) v {
		return [Vector divideVector: v through: [Vector lengthOfVector: v]];
	}
	+ (Vector2D) turnVectorLeft: (Vector2D) v {
		Vector2D w;
		w.vx = v.vy*-1;
		w.vy = v.vx;
		return w;
	}
	+ (Vector2D) turnVectorRight: (Vector2D) v {
		Vector2D w;
		w.vx = v.vy;
		w.vy = v.vx*-1;
		return w;
	}
	
//Point2D related Code
	+ (Point2D) makePoint {
		Point2D p;
		return p;
	}
	+ (Point2D) makePointWithx: (float) x y: (float) y {
		Point2D p;
		p.x = x;
		p.y = y;
		return p;
	}

	+ (Vector2D) point: (Point2D) p toPoint: (Point2D) q {
		Vector2D v;
		v.vx = q.x-p.x;
		v.vy = q.y-p.y;
		return v;
	}
	+ (Point2D) addVector: (Vector2D) v toPoint: (Point2D) p {
		p.x += v.vx;
		p.y += v.vy;
		return p;
	}
	+ (Point2D) subtractVector: (Vector2D) v fromPoint: (Point2D) p {
		p.x-=v.vx;
		p.y-=v.vy;
	}
	+ (BOOL) point: (Point2D) p equalsPoint: (Point2D) q withMaxDistance: (float) d {
		return ([Vector lengthOfVector: [Vector point: p toPoint: q]]<=d);
	}

//Line2D related Code
	+ (Line2D) makeLine {
		Line2D l;
		return l;
	}
	+ (Line2D) makeLineWithPoint:(Point2D) p andPoint: (Point2D) q {
		Line2D l;
		l.p = p;
		l.q = q;
		return l;
	}
	+ (Line2D) makeLineWithPoint:(Point2D) p andVector: (Vector2D) v {
		Line2D l;
		l.p = p;
		l.q = [Vector addVector: v toPoint: p];
		return l;
	}
	+ (Line2D) makeLineWithpx: (float)px py: (float)py qx: (float)qx qy: (float)qy {
		Line2D l;
		l.p.x = px;
		l.p.y = py;
		l.q.x = qx;
		l.q.y = qy;
	}

	+ (float) lengthOfLine: (Line2D) l {
		return [Vector lengthOfVector: [Vector point: l.p toPoint: l.q]];
	}
	+ (Gerade2D) convertLineToGerade: (Line2D) l {
		return [Vector makeGeradeWithPoint: l.p andPoint: l.q];
	}

//Gerade2D related Code
	+ (Gerade2D) makeGerade {
		Gerade2D g;
		return g;
	}
	+ (Gerade2D) makeGeradeWithPoint: (Point2D) p andVector: (Vector2D) v {
		Gerade2D g;
		g.p = p;
		g.v = v;
		return g;
	}
	+ (Gerade2D) makeGeradeWithPoint: (Point2D) p andPoint: (Point2D) q {
		Gerade2D g;
		g.p = p;
		g.v = [Vector point: p toPoint: q];
		return g;
	}

	+ (Point2D) cutGerade:(Gerade2D) g withGerade:(Gerade2D) h {
		float lambda = (g.p.y * h.v.vx - h.p.y * h.v.vx - g.p.x * h.v.vy + h.p.x * h.v.vy) /
			(g.v.vx * h.v.vy - g.v.vy * h.v.vx);
		return [Vector 
				makePointWithx: (g.p.x + lambda * g.v.vx)
							 y: (g.p.y + lambda * g.v.vy)
				];
	}

//Transformer2D related Code
	+ (Transformer2D) makeTransformerWithLine: (Line2D) rec1 andLine: (Line2D) rec2 {
		return [Vector makeTransformerWithp1: rec1.p q1: rec1.q p2: rec2.p q2: rec2.q];
	}
	//If that works, I'll eat a Besen.
	+ (Transformer2D) makeTransformerWithp1: (Point2D) p1 q1: (Point2D) q1 
										 p2: (Point2D) p2 q2: (Point2D) q2 {
		Transformer2D t;
			
		
		float olength = [Vector lengthOfVector: [Vector point: p1 toPoint: q1]];
		Point2D s = 
		[Vector 
			cutGerade: [Vector makeGeradeWithPoint: p1 andPoint: q1]
			withGerade: [Vector makeGeradeWithPoint: p2 andVector: 
				[Vector turnVectorLeft:[Vector point: p1 toPoint: q1]]
			]
		];
		t.t1 = [Vector lengthOfVector: [Vector point: p1 toPoint: s]] / olength;

		//This is really difficult to read cocoa code...
		//Translated to normal math/english: The point (p1+(p1->q1)*t1) may not be equal to s. 
		//The comparison uses point: equalsPoint: withMaxDistance: because of rounding errors.
		//The whole thing is a workaround for a mathematical problem - there are no negative proportions.
		if (![Vector point:
				[Vector addVector:
					[Vector multiplyVector:
						[Vector point: p1 toPoint: q1]
					with: t.t1]
				toPoint: p1]
			equalsPoint: s withMaxDistance: 0.01f]
		) {
			t.t1*=-1;
		}

		t.o1 = [Vector lengthOfVector: [Vector point: s toPoint: p2]] / olength;

		if (![Vector point:
				[Vector addVector:
					[Vector turnVectorLeft:
						[Vector multiplyVector:
							[Vector point: p1 toPoint: q1]
						with: t.o1]
				] toPoint: s]
			equalsPoint: p2 withMaxDistance: 0.01f]
		) {
			t.o1*=-1;
		}

		//Second step
		s = 
		[Vector 
			cutGerade: [Vector makeGeradeWithPoint: p1 andPoint: q1]
			withGerade: [Vector makeGeradeWithPoint: q2 andVector: 
				[Vector turnVectorLeft:[Vector point: p1 toPoint: q1]]
			]
		];
		t.t2 = [Vector lengthOfVector: [Vector point: p1 toPoint: s]] / olength;

		if (![Vector point:
				[Vector addVector:
					[Vector multiplyVector:
						[Vector point: p1 toPoint: q1]
					with: t.t2]
				toPoint: p1]
			equalsPoint: s withMaxDistance: 0.01f]
		) {
			t.t2*=-1;
		}

		t.o2 = [Vector lengthOfVector: [Vector point: s toPoint: q2]] / olength;
		if (![Vector point:
				[Vector addVector:
					[Vector turnVectorLeft:
						[Vector multiplyVector:
							[Vector point: p1 toPoint: q1]
						with: t.o2]
				] toPoint: s]
			equalsPoint: q2 withMaxDistance: 0.01f]
		) {
			t.o2*=-1;
		}
		return t;
	}

	+ (Line2D) transformLine: (Line2D) l withTransformer: (Transformer2D) t {
		Vector2D o = [Vector point: l.p toPoint: l.q];
		
		return 
		[Vector makeLineWithPoint:
			[Vector addVector: 
				[Vector addVector: 
					[Vector multiplyVector: o with: t.t1]
				toVector:
					[Vector multiplyVector: 
						[Vector turnVectorLeft: o]
					with: t.o1]
				]
			toPoint: l.p]
		andPoint:
			[Vector addVector: 
				[Vector addVector: 
					[Vector multiplyVector: o with: t.t2]
				toVector:
					[Vector multiplyVector: 
						[Vector turnVectorLeft: o]
					with: t.o2]
				]
			toPoint: l.p]	
		];
	}
	
@end